<?php

/**
 * Created by PhpStorm.
 * User: tom_xia
 * Date: 2017/4/18
 * Time: 12:11
 * @desc文件上传类
 */
class Upload_file
{
    //基本上传文件配置
    protected $formName;//上传文件时，文件表单的名字
    protected $fileName;//文件最终保存名称前缀,例如1493100891.txt ===>的1493100891前缀
    protected $uploadPath;//保存路径,1.如果不分片，这个就是最终的保存路径，2.如果分片，这就是临时保存路径
    protected $allowType;//允许上传类型用逗号分割，*号代表全部，例如*和jpg,jpeg,gif
    protected $realFileName;//真实文件名称，上传文件的真实文件名称
    protected $finalFileName="";//合并文件之后生成的文件名称,最终文件存放名称。
    protected $maxSize = 0;//最大的文件大小，0为不限制
    //错误信息
    protected $error;

    public function __construct(array $config)
    {
       if(!array_key_exists("filename",$config)){
           throw new Exception("缺少文件名");
       }
       if(!array_key_exists("uploadpath",$config)){
           throw new Exception("缺少文件保存路径");
       }
       if(!array_key_exists("allowtype",$config)){
           $this->allowType = "*";
       }
       $this->fileName = $config["filename"];
       $this->uploadPath = $config["uploadpath"];
       $this->allowType = $config["allowtype"];
       $this->realFileName = isset($config["realfilename"]) ? $config["realfilename"] : null;
       $this->maxSize = isset($config["maxsize"]) ? $config["maxsize"] : 0;

       if(isset($config["formname"])){
           $this->formName = $config["formname"];
       }else{
           $this->formName = "filename";
       }
       //开启分片的配置
        if(isset($config["ischunk"]) && $config["ischunk"]){
           //检查必要参数
            if(empty($config["index"]) || empty($config["total"]) ||
                empty($config["redis"]) || empty($config["fuid"]) || empty($config["savepath"])){
                throw new Exception("分片配置错误");
            }
            $this->isChunk = true;
            $this->index = $config["index"];
            $this->total = $config["total"];
            $this->fuid = $config["fuid"];
            $this->savePath = $config["savepath"];
            $this->redis = $config["redis"];
            //如果开启分片，那么需要真实文件名配置
            if(!isset($config["realfilename"])){
                throw new Exception("分片配置缺少真实文件名");
            }
        }

    }

    public function getError()
    {
        return $this->error;
    }
    public function doupload()
    {
        $errorCode = $_FILES[$this->formName]["error"];
        if($errorCode == 0){
            $isPostUpload = is_uploaded_file($_FILES[$this->formName]["tmp_name"]);
            if(!$isPostUpload){
                $this->error = "文件不安全";
                return false;
            }
            //检查文件类型
            $isAllowType = $this->checkFileType($_FILES[$this->formName]["name"]);
            if(!$isAllowType){
                $this->error = "不允许上传的文件类型,允许上传的文件类型是:{$this->allowType}";
                return false;
            }
            //检查上传文件大小
            if($this->maxSize>0 && $this->maxSize < $_FILES[$this->formName]['size'] ){                
                $this->error = "文件不能超过:" . ($this->maxSize/1024) . "KB";
                return false;
            }
            //判断要保存的路径是否存在,如果不存在则创建
            if(!file_exists($this->uploadPath)){
                mkdir($this->uploadPath,0744,true);
            }
            if(!$this->realFileName){
                $ext = pathinfo($_FILES[$this->formName]["name"],PATHINFO_EXTENSION);
            }else{
                $ext = pathinfo($this->realFileName,PATHINFO_EXTENSION);
            }
            if(empty($ext)){
                $ext = "png";
            }
            $destination = $this->isChunk ? rtrim($this->uploadPath,"/")."/".$this->fuid."_".$this->index : rtrim($this->uploadPath,"/")."/".$this->fileName.".".$ext;
//            var_dump($_FILES[$this->formName]["tmp_name"],$destination);exit;
            $moveSuccess = move_uploaded_file($_FILES[$this->formName]["tmp_name"],$destination);
            if(!$moveSuccess){
                $this->error = "文件不合法或者移动文件失败";
                return false;
            }else{
                if($this->isChunk){
                    $this->redisIndex = $this->redis->incrby("JPER_UPLOAD_INDEX_".$this->fuid,1);
                }else{
                    $this->finalFileName = $this->fileName.".".$ext;
                }
                return true;
            }

        }else{
            $this->setErrorByCode($errorCode);
            return false;
        }
    }

    /**
     * @desc 设置错误码的错误信息
     * @param $errCode 错误码
     */
    public function setErrorByCode($errCode)
    {
        switch($errCode)
        {
            case 1:
                $max_size = ini_get("upload_max_filesize");
                $this->error = "文件上传大小超过限制:{$max_size}";
                break;
            case 2:
                $this->error = "超过表单上传大小";
                break;
            case 3:
                $this->error = "部分文件上传成功";
                break;
            case 4:
                $this->error = "没有文件被上传";
                break;
            case 6:
                $this->error = "找不到上传的临时文件";
                break;
            case 7:
                $this->error = "写入临时文件失败";
                break;
        }
    }

    /**
     * @desc 检查文件是否是允许上传的类型
     * @param $filename 要检查的文件名称
     * @return bool
     */
    public function checkFileType($filename)
    {
        if($this->allowType === "*"){
            return true;
        }else{
            $allowType = explode(",",$this->allowType);
            $fileExt = strtolower(pathinfo($filename,PATHINFO_EXTENSION));
            if(in_array($fileExt,$allowType)){
                return true;
            }else{
                return false;
            }
        }
    }
    //分片上传配置
    public $isChunk=false;//是否开启分片
    public $index;//当前是第几个分片
    public $total;//总共多少分片
    public $fuid;//分片文件的file uuid
    public $savePath;//分片合并后保存的路径
    public $redis;//redis对象

    //无须配置的属性
    public $redisIndex;
    /**
     * @desc 分片上传
     */
    public function mergeUpload()
    {
        if(!$this->isChunk){
            $this->error = "未开启分片";
            return false;
        }
        $filename = $this->realFileName;
        $ext = pathinfo($filename, PATHINFO_EXTENSION);
        if(empty($ext)){
            $ext = "png";
        }
        $name = $this->fileName . "." . $ext;
        //如果保存的路劲不存在，则创建
        if(!file_exists($this->savePath)){
            mkdir($this->savePath,0744,true);
        }
        $desFileNameP = fopen(rtrim($this->savePath,"/"). "/".$name, "w");
        for ($i = 1; $i <= $this->total; $i++) {
            $file = $this->uploadPath . $this->fuid . "_" . $i;
            $strStream = file_get_contents($file);
            fwrite($desFileNameP, $strStream);
            unset($strStream);
            unlink($file);
            $this->redis->incrBy("JPER_UPLOAD_INDEX_".$this->fuid, -1);
        }
        fclose($desFileNameP);
        $this->redis->del("JPER_UPLOAD_INDEX_".$this->fuid);
        if(file_exists(rtrim($this->savePath,"/"). "/".$name)){
            $this->finalFileName = $name;
            return true;
        }else{
            $this->error = "合并文件失败";
            return false;
        }
    }

    /**
     * @desc 获取上传文件的最终文件名称，包括后缀
     * @return string
     */
    public function getFinalFileName()
    {
        return $this->finalFileName;
    }
}